﻿using Microsoft.AspNetCore.Mvc;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;

namespace ApiLib.Utils;

/// <summary>
/// Echarts工具类
/// </summary>
public static class EchartsUtil
{
    /// <summary>
    /// 导出
    /// </summary>
    /// <param name="info"></param>
    /// <returns></returns>
    public static FileResult ToExport(this EchartsInfo info)
    {
        var ms = info.ExportToExcel().Write();
        return ms.ToExport($"{info.Title?.Text ?? "Echarts数据"}_{DateTime.Now:yyyyMMddHHmmss}.xlsx");
    }

    /// <summary>
    /// 常规图表转Excel
    /// </summary>
    /// <param name="info"></param>
    /// <returns></returns>
    public static IWorkbook ExportToExcel(this EchartsInfo info)
    {
        var workbook = new XSSFWorkbook();
        var sheet = workbook.CreateSheet(info.Title?.Text ?? "Echarts数据");

        var colIndex = 0;
        var rowIndex = 0;
        var row = sheet.CreateRow(rowIndex++); //在工作表中添加一行
        row.CreateCell(colIndex++);
        var titles = info.XAxis.Data.IsNullOrEmpty() ? info.YAxis.Data : info.XAxis.Data;
        if (titles != null)
        {
            foreach (var key in titles)
            {
                row.CreateCell(colIndex++).SetCellValue(key);
            }
        }

        if (info.Series != null)
        {
            foreach (var series in info.Series)
            {
                colIndex = 0;
                row = sheet.CreateRow(rowIndex++);
                row.CreateCell(colIndex++).SetCellValue(series.Name);
                if (series.Data != null)
                {
                    foreach (object value in series.Data)
                    {
                        row.CreateCell(colIndex++).SetCellValue(value.ToString());
                    }
                }
            }
        }

        return workbook;
    }
}

/// <summary>
/// Echarts对象
/// </summary>
public class EchartsInfo
{
    /// <summary>
    /// 标题
    /// </summary>
    public TitleInfo? Title { get; set; }

    /// <summary>
    /// Legend
    /// </summary>
    public LegendInfo? Legend { get; set; }

    /// <summary>
    /// 直角坐标系内绘图网格
    /// </summary>
    public GridInfo? Grid { get; set; }

    /// <summary>
    /// 提示框
    /// </summary>
    public TooltipInfo? Tooltip { get; set; }

    /// <summary>
    /// x轴
    /// </summary>
    public AxisInfo XAxis { get; set; } = new();

    /// <summary>
    /// y轴
    /// </summary>
    public AxisInfo YAxis { get; set; } = new();

    /// <summary>
    /// 图例
    /// </summary>
    public List<SeriesInfo>? Series { get; set; }

    /// <summary>
    /// 文字样式，不包括Legend
    /// </summary>
    public TextStyleInfo? TextStyle { get; set; }

    /// <summary>
    /// 提示框组件
    /// </summary>
    public class TooltipInfo
    {
        /// <summary>
        /// 是否显示提示框组件。
        /// </summary>
        public bool Show { get; set; } = true;

        /// <summary>
        /// 触发类型<br/>
        /// <example>
        /// <list type="bullet">
        ///   <item>item：数据项图形触发，主要在散点图，饼图等无类目轴的图表中使用。</item>
        ///   <item>axis：坐标轴触发，主要在柱状图，折线图等会使用类目轴的图表中使用。</item>
        ///   <item>none：什么都不触发。</item>
        /// </list>
        /// </example>
        /// </summary>
        public string Trigger { get; set; } = "axis";

        /// <summary>
        /// 显示模版  '{b0}: {c0}<br/>{b1}: {c1}'
        /// </summary>
        public string? Formatter { get; set; }
    }

    /// <summary>
    /// 标题
    /// </summary>
    public class TitleInfo
    {
        /// <summary>
        /// 标题
        /// </summary>
        public string? Text { get; set; }
    }

    /// <summary>
    /// 
    /// </summary>
    public class LegendInfo
    {
        /// <summary>
        /// 数据集
        /// </summary>
        public List<string>? Data { get; set; }

        /// <summary>
        /// 文字样式
        /// </summary>
        public TextStyleInfo? TextStyle { get; set; }

        /// <summary>
        /// 图例组件离容器上侧的距离。
        /// </summary>
        public object Top { get; set; } = "auto";

        /// <summary>
        /// 图例组件离容器下侧的距离。
        /// </summary>
        public object Bottom { get; set; } = "auto";

        /// <summary>
        /// 图例组件离容器左侧的距离。
        /// </summary>
        public object Left { get; set; } = "auto";

        /// <summary>
        /// 图例组件离容器右侧的距离。
        /// </summary>
        public object Right { get; set; } = "auto";

        /// <summary>
        /// 图例组件的高度。默认自适应。
        /// </summary>
        public object Height { get; set; } = "auto";

        /// <summary>
        /// 图例组件的宽度。默认自适应。
        /// </summary>
        public object Width { get; set; } = "auto";

        /// <summary>
        /// 图例标记和文本的对齐。默认自动，根据组件的位置和 orient 决定，当组件的 left 值为 'right' 以及纵向布局（orient 为 'vertical'）的时候为右对齐，即为 'right'。
        /// </summary>
        public object Align { get; set; } = "auto";

        /// <summary>
        /// 图例标记的图形宽度。
        /// </summary>
        public int ItemWidth { get; set; } = 25;

        /// <summary>
        /// 图例标记的图形高度。
        /// </summary>
        public int ItemHeight { get; set; } = 14;
    }

    /// <summary>
    /// 坐标信息
    /// </summary>
    public class AxisInfo
    {
        /// <summary>
        /// 类型
        /// <example>
        /// <list type="bullet">
        ///   <item>'value' 数值轴，适用于连续数据。</item>
        ///   <item>'category' 类目轴，适用于离散的类目数据。为该类型时类目数据可自动从 series.data 或 dataset.source 中取，或者可通过 xAxis.data 设置类目数据。</item>
        ///   <item>'time' 时间轴，适用于连续的时序数据，与数值轴相比时间轴带有时间的格式化，在刻度计算上也有所不同，例如会根据跨度的范围来决定使用月，星期，日还是小时范围的刻度。</item>
        ///   <item>'log' 对数轴。适用于对数数据。对数轴下的堆积柱状图或堆积折线图可能带来很大的视觉误差，并且在一定情况下可能存在非预期效果，应避免使用。</item>
        /// </list>
        /// </example>
        /// </summary>
        public string Type { get; set; } = "category";

        /// <summary>
        /// 坐标轴名称。
        /// </summary>
        public string? Name { get; set; }

        /// <summary>
        /// 类目数据，在类目轴（type: 'category'）中有效。
        /// </summary>
        public List<string>? Data { get; set; }
    }

    /// <summary>
    /// 图例
    /// </summary>
    public class SeriesInfo
    {
        /// <summary>
        /// 数据
        /// </summary>
        public List<object>? Data { get; set; }

        /// <summary>
        /// 类型:line,bar,pie
        /// </summary>
        public string Type { get; set; } = "bar";

        /// <summary>
        /// 柱状图的柱子宽度
        /// </summary>
        public object BarWidth { get; set; } = "auto";

        /// <summary>
        /// 系列名称，用于tooltip的显示，legend 的图例筛选，在 setOption 更新数据和配置项时用于指定对应的系列。
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 数据堆叠，同个类目轴上系列配置相同的 stack 值可以堆叠放置。
        /// <para>目前 stack 只支持堆叠于 'value' 和 'log' 类型的类目轴上。</para>
        /// </summary>
        public string Stack { get; set; } = "total";

        /// <summary>
        /// 样式
        /// </summary>
        public ItemStyleInfo? ItemStyle { get; set; }

        /// <summary>
        /// 样式
        /// </summary>
        public TextStyleInfo? TextStyle { get; set; }
    }

    /// <summary>
    /// 样式
    /// </summary>
    public class ItemStyleInfo
    {
        /// <summary>
        /// 边框圆角
        /// </summary>
        public string Color { get; set; } = "auto";

        /// <summary>
        /// 边框宽度
        /// </summary>
        public int BorderWidth { get; set; } = 0;

        /// <summary>
        /// 边框类型。
        /// <para>柱条的描边类型，默认为实线，支持 'solid', 'dashed', 'dotted'</para>
        /// </summary>
        public string BorderType { get; set; } = "solid";

        /// <summary>
        /// 边框颜色
        /// </summary>
        public string BorderColor { get; set; } = "#000";

        /// <summary>
        /// 阴影颜色
        /// </summary>
        public string? ShadowColor { get; set; }
    }

    /// <summary>
    /// 样式
    /// </summary>
    public class TextStyleInfo
    {
        /// <summary>
        /// 边框圆角
        /// </summary>
        public string Color { get; set; } = "#fff";

        /// <summary>
        /// 字体大小
        /// </summary>
        public int FontSize { get; set; } = 12;

        /// <summary>
        /// 字体风格：normal、italic、oblique
        /// </summary>
        public string FontStyle { get; set; } = "normal";

        /// <summary>
        /// 字体粗细：normal、bold、bolder、lighter
        /// </summary>
        public string FontWeight { get; set; } = "normal";

        /// <summary>
        /// 字体
        /// </summary>
        public string FontFamily { get; set; } = "sans-serif";

        /// <summary>
        /// 行高
        /// </summary>
        public int? LineHeight { get; set; }

        /// <summary>
        /// 文字显示宽度
        /// </summary>
        public int? Weight { get; set; }

        /// <summary>
        /// 文字显示高度
        /// </summary>
        public int? Height { get; set; }

        /// <summary>
        /// 超出<br/>
        /// runcate截断，并在末尾显示ellipsis配置的文本，默认为...<br/>
        /// break 换行<br/>
        /// breakAll 换行，跟'break'不同的是，在英语等拉丁文中，'breakAll'还会强制单词内换行
        /// </summary>
        public string Overflow { get; set; } = "none";

        /// <summary>
        /// 在overflow配置为'truncate'的时候，可以通过该属性配置末尾显示的文本。
        /// </summary>
        public string Ellipsis { get; set; } = "...";
    }

    /// <summary>
    /// 直角坐标系内绘图网格
    /// </summary>
    public class GridInfo
    {
        /// <summary>
        /// grid 组件离容器左侧的距离。<br/>
        /// 可以是像 20 这样的具体像素值，可以是像 '20%' 这样相对于容器高宽的百分比，也可以是 'left', 'center', 'right'。
        /// </summary>
        public object Left { get; set; } = "10%";

        /// <summary>
        /// grid 组件离容器右侧的距离。
        /// </summary>
        public object Right { get; set; } = "10%";

        /// <summary>
        /// grid 组件离容器上侧的距离。
        /// </summary>
        public object Top { get; set; } = 60;

        /// <summary>
        /// grid 组件离容器下侧的距离。
        /// </summary>
        public object Bottom { get; set; } = 60;

        /// <summary>
        /// grid 组件的宽度。默认自适应。
        /// </summary>
        public object Width { get; set; } = "auto";

        /// <summary>
        /// grid 组件的高度。默认自适应。
        /// </summary>
        public object Height { get; set; } = "auto";

        /// <summary>
        /// grid 区域是否包含坐标轴的刻度标签。
        /// </summary>
        public bool ContainLabel { get; set; } = false;
    }
}